package com.zhanglu.fun.services.website.security;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.zhanglu.fun.services.website.bean.response.IsLogin;
import com.zhanglu.fun.services.website.client.AuthenticationClient;
import com.zhanglu.fun.services.website.login.LoginService;
import com.zhanglu.fun.services.website.security.response.MsgCode;
import com.zhanglu.fun.services.website.security.response.SecurityResponse;
import com.zhanglu.fun.tookit.json.JsonTools;
import com.zhanglu.fun.tookit.jwt.JwtUtil;
import com.zhanglu.fun.tookit.jwt.OldJwtUtil;
import com.zhanglu.fun.tookit.jwt.TokenInfo;

import lombok.extern.slf4j.Slf4j;

/**
 * @description 权限拦截器
 * @Auther zhanglu
 * @Date 2017/9/19 下午3:42
 */
@Slf4j
public class SecurityInterceptor implements HandlerInterceptor {
    @Autowired
    private AuthenticationClient authenticationClient;
    @Autowired
    private LoginService loginService;

    // 在执行目标方法之前执行
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        log.debug("The object method is " + o.getClass().getName());
        Cookie userCookie;
        Cookie[] cookies = httpServletRequest.getCookies();
        userCookie = cookies != null && cookies.length > 0 ? Arrays.stream(cookies).
                map(cookie -> cookie.getName().equals("user_token") && !StringUtils.isBlank(cookie.getValue()) ? cookie : null).
                filter(Objects::nonNull).findFirst().orElse(null) : null;

        SecurityResponse response = new SecurityResponse(); // 回执消息
        response.setMsgCode(MsgCode.SYSTEM_ERROR.getCode());
        response.setDescription(MsgCode.SYSTEM_ERROR.getMessage());

        HandlerMethod handler = (HandlerMethod) o;
        Secured securedClass = handler.getBeanType().getAnnotation(Secured.class);// 获取类的权限
        if (!security(httpServletResponse, securedClass, userCookie, "class", response)) {
            writeResponse(httpServletResponse, response);
            return false;
        }

        Secured securedMethod = handler.getMethodAnnotation(Secured.class);
        if (!security(httpServletResponse, securedMethod, userCookie, "method", response)) {
            writeResponse(httpServletResponse, response);
            return false;
        }

        return true;
    }

    // 执行目标方法之后执行
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    // 在请求已经返回之后执行
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }

    private boolean security(HttpServletResponse httpServletResponse, Secured secured, Cookie userCookie, String logType, SecurityResponse response) {
        if (null != secured && secured.value().length > 0) { // 这里判断一下类有没有权限，有的话要对比
            log.debug("the login cookie token is {}", Objects.isNull(userCookie) ? "null" : userCookie.getValue());
            log.debug("The {} security has {}", logType, Arrays.asList(secured.value()));
            if (!Objects.isNull(userCookie)) {
                if (isTokenExpire(userCookie.getValue())) {
                    IsLogin isLogin = authenticationClient.isLogin(userCookie.getValue());
                    log.debug("{} is login {}", logType, isLogin.getLogin());
                    if (!isLogin.getLogin()) {
                        response.setMsgCode(MsgCode.TOKEN_TIME_OUT.getCode());
                        response.setDescription(MsgCode.TOKEN_TIME_OUT.getMessage());
                        return false;
                    } else {
                        loginService.setCookie(httpServletResponse,isLogin.getToken());
                        log.debug("reset token:{}", isLogin.getToken());
                    }
                }

                List<String> roles = Arrays.asList(OldJwtUtil.parseJWT(userCookie.getValue()).getRoles().split(","));
                log.debug("this login has roles are :{}", roles);
                Set<String> roleSet = Arrays.stream(secured.value()).collect(Collectors.toSet());
                int count = roles.stream().mapToInt(role -> roleSet.contains(role) ? 1 : 0).sum();
                log.debug("{} security contains login's security count is {}", logType, count);
                if (count <= 0) { // 没有此权限
                    log.debug("The login has no role to call this {}", logType);
                    response.setMsgCode(MsgCode.NO_ROLES.getCode());
                    response.setDescription(MsgCode.NO_ROLES.getMessage());
                    return false;
                }
            } else {
                log.debug("When the login call this {} ，cookie is null", logType);
                response.setMsgCode(MsgCode.COOKIE_TIME_OUT.getCode());
                response.setDescription(MsgCode.COOKIE_TIME_OUT.getMessage());
                return false;
            }
        }

        return true;
    }

    private boolean isTokenExpire(String token) { //本地token对比。token中日期和数据库中日期存在10分钟间隔。这里过期再去查询数据库
        TokenInfo tokenInfo = OldJwtUtil.parseJWT(token);
        log.debug("the token info is {}", JsonTools.showJSONStringFormat(tokenInfo));
        long tokenTime = tokenInfo.getExpiresDate().getTime();
        long nowTime = System.currentTimeMillis();
        return nowTime > tokenTime;
    }

    private void writeResponse(HttpServletResponse response, SecurityResponse securityResponse) {
        PrintWriter writer = null;
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html; charset=utf-8");
        try {
            writer = response.getWriter();
            writer.print(JsonTools.toJSONString(securityResponse));
        } catch (IOException e) {
            log.error("response error", e);
        } finally {
            if (writer != null)
                writer.close();
        }
    }
}
